﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace LGPeWJNM.TeitzEBart
{
    public class TeitzEBart: LGPeWJNM.BaseClasses.IPMediana
    {
        private Random rnd { get; set; }

        public TeitzEBart()
        {
            rnd = new Random();
        }

        /// <summary>
        /// Determina medianas aos pontos do plano cartesiano de acordo com suas demandas.
        /// </summary>
        /// <remarks>Implementado pelo método de <i>Teitz e Bart</i></remarks>
        /// <param name="destinos">Conjuntos desordenado de coordenadas de destinos</param>
        /// <param name="quantidadeVeiculos">Número de veículos disponíveis para a roteirização</param>
        /// <param name="capacidadeVeiculo">Capacidade de carga de cada veículo</param>
        /// <returns>Coordenadas das medianas dos clientes</returns>
        public IEnumerable<LGPeWJNM.BaseClasses.Point> Processar(IEnumerable<LGPeWJNM.BaseClasses.ICliente> destinos, int quantidadeVeiculos, float capacidadeVeiculo)
        {
            var medianas = destinos.OrderBy(r => rnd.Next()).Take(quantidadeVeiculos).ToList();

            var naoAnalisados = (from d in destinos
                                 where !medianas.Contains(d)
                                 select d).ToList();

            do
            {
                var q = naoAnalisados.AsParallel().WithExecutionMode(ParallelExecutionMode.ForceParallelism).Select(nA => medianas.Select(m =>
                {
                    var novaMediana = medianas.ToList();
                    novaMediana.Remove(m);
                    novaMediana.Add(nA);
                    return new { reducao = medianas.numeroDeTransmissao(destinos) - novaMediana.numeroDeTransmissao(destinos), pontoMediana = m, pontoAnalisado = nA };
                }).OrderBy(o => o.reducao).Last()).OrderBy(o => o.reducao).Last();
                if (q.reducao <= 0)
                    break;
                else
                {
                    medianas.Remove(q.pontoMediana);
                    medianas.Add(q.pontoAnalisado);

                    naoAnalisados.Remove(q.pontoAnalisado);
                    naoAnalisados.Add(q.pontoMediana);
                }
            } while (true);

            return medianas.Select(s => s.Coordenada);
        }
    }
}
